let core = require('@babel/core');
let types = require('babel-types');
let BabelPluginTransformEs2015ArrowFunctions = require('babel-plugin-transform-es2015-arrow-functions');

const sourceCode = `
const sum = (a,b)=>{
  console.log(this);
  return a+b;
}
`

// babel插件就是一个对象,它会有一个visitor访问器
let BabelPluginTransformEs2015ArrowFunctions2 = {
  visitor: {
    // 属性就是节点的类型,Babel在遍历到对应类型的节点时,会调用此函数
    ArrowFunctionExpression(nodePath) { // nodepath是参数的所有路径
      // console.log('nodepath', nodePath)
      let { node } = nodePath;
      // 处理this的指针问题
      hoistFunctionEnvironment(nodePath);
      node.type = 'FunctionExpression'; // 将将头函数的type:'ArrowFunctionExpression',更改为普通函数的
    }
  }
}

function hoistFunctionEnvironment(fnPath) {
  // 1从nodepath向上查找this的作用域
  const thisEnvFn = fnPath.findParent(p => {
    // 看是否存在函数作用域且不是箭头函数,或者全局作用域-->它们就是this的所在的作用域
    return (p.isFunction() && !p.isArrowFunctionExpression()) || p.isProgram()
  })
  //2查找当前箭头函数作用域中那些地方用到了this
  let thisPaths = getScopeInfoInformation(fnPath);

  //3声明一个变量_this = this
  let thisBinding = "_this"
  // 如果子节点中有this调用
  if (thisPaths.length > 0) {
    //4.生成var _this = this
    thisEnvFn.scope.push({
      id: types.identifier(thisBinding),
      init: types.thisExpression()
    })
    //5修改变量
    thisPaths.forEach(thisPath => {
      let thisBindingIdentifier = types.identifier(thisBinding);
      // 把此路径上面挂载的节点进行替换
      thisPath.replaceWith(thisBindingIdentifier)
    })
  }
}

let targetCode = core.transform(sourceCode, {
  plugins: [BabelPluginTransformEs2015ArrowFunctions2]
})


function getScopeInfoInformation(fnPath) {
  let thisPaths = [];
  // 遍历当前路径的子路径
  // 从当前路径向下查找,如果遇到ThisExpression节点就把它的路径添加到thisPaths数组里
  fnPath.traverse({
    ThisExpression(thisPath) { // 识别使用到this的位置
      thisPaths.push(thisPath)
    }
  })
  return thisPaths;
}
console.log('targetCode.code', targetCode.code)